
from WtfEngine import *;
import WtfFx;
import math;

# Global scale factor
scale = 100;


#### Dimensions of the table ####

chunkiness = (1.0 / 40.0);
ratio = 0.5;
h = 0.15 * scale; w = scale; d = w * ratio;
pi = 3.14159

# Y position of attachment point of men to bar
yattach = 0.01 * scale;

baseH = h / 4;
slopeH = 0.01 * scale;
slopeD = w * chunkiness * 1.4;
cornerW = w / 6;
slopeAng = pi / 8;
gw = (d - (w * chunkiness)) * 0.3;
gh = h * 0.75;
gd = w * chunkiness * 3;



#### Physical and graphical models ####

redMaterial = MaterialCreationOptions();
redMaterial.diffuse = ColourVal(0.8, 0.8, 0.8, 0.0);
redMaterial.power = 0;

blueMaterial = MaterialCreationOptions();
blueMaterial.diffuse = ColourVal(0.2, 0.2, 0.8, 1.0);
baseMaterial = MaterialCreationOptions();
baseMaterial.diffuse = ColourVal(0.2, 0.5, 0.2, 0.0);
baseMaterial.specular = Vector(1.0,1.0,1.0);
baseMaterial.power = 0;
ballMaterial = MaterialCreationOptions();
ballMaterial.diffuse = ColourVal(0.8, 0.8, 0.8, 0.9);
woodMaterial = MaterialCreationOptions();
woodMaterial.diffuse = ColourVal(0.53, 0.37, 0.25, 1.0);

DebugPrint("Loading graphics...");


gModels = {
	'red': Material("gfx_wtf_red", redMaterial),
	'blue': Material("gfx_wtf_blue", blueMaterial),
	'baseMat': Material("gfx_wtf_base", baseMaterial),
	'ballMat': Material("gfx_wtf_ballMat", ballMaterial),
    'woodMat': Material("gfx_wtf_woodMat", woodMaterial),

	'ball': SphereModel("pmodel_wtf_ball", scale * 0.012, 16),
	'ballMesh': PrimitiveModel("gfx_wtf_ball"),
	'ballGfx': MaterialBinding("mbBallGfx", MaterialRef("gfx_wtf_ballMat"), MeshRef("gfx_wtf_ball")),
		
	'man': OblongModel("pmodel_wtf_man", Vector(scale * 0.016, scale * 0.092, scale * 0.024)),
	'manGfx': XModel("media/meshes/player.x"),
	'bar': CylinderModel("pmodel_wtf_bar", scale * 0.005, d + (0.2 * scale), 16),
	'barGfx': XModel("media/meshes/bar.x"),
	
	'rowA1': CompoundModel("pmodel_wtf_row_a1"),
	'rowA2': CompoundModel("pmodel_wtf_row_a2"),
	'rowA3': CompoundModel("pmodel_wtf_row_a3"),
	'rowA5': CompoundModel("pmodel_wtf_row_a5"),
	'rowA1Mesh': Model("gfx_wtf_row_a1m"),
	'rowA2Mesh': Model("gfx_wtf_row_a2m"),
	'rowA3Mesh': Model("gfx_wtf_row_a3m"),
	'rowA5Mesh': Model("gfx_wtf_row_a5m"),
	'rowA1Gfx': MaterialBinding("gfx_wtf_row_a1", MaterialRef("gfx_wtf_red"), ModelRef("gfx_wtf_row_a1m")),
	'rowA2Gfx': MaterialBinding("gfx_wtf_row_a2", MaterialRef("gfx_wtf_red"), ModelRef("gfx_wtf_row_a2m")),
	'rowA3Gfx': MaterialBinding("gfx_wtf_row_a3", MaterialRef("gfx_wtf_red"), ModelRef("gfx_wtf_row_a3m")),
	'rowA5Gfx': MaterialBinding("gfx_wtf_row_a5", MaterialRef("gfx_wtf_red"), ModelRef("gfx_wtf_row_a5m")),
	'rowB1': CompoundModel("pmodel_wtf_row_b1"),
	'rowB2': CompoundModel("pmodel_wtf_row_b2"),
	'rowB3': CompoundModel("pmodel_wtf_row_b3"),
	'rowB5': CompoundModel("pmodel_wtf_row_b5"),
	'rowB1Mesh': Model("gfx_wtf_row_b1m"),
	'rowB2Mesh': Model("gfx_wtf_row_b2m"),
	'rowB3Mesh': Model("gfx_wtf_row_b3m"),
	'rowB5Mesh': Model("gfx_wtf_row_b5m"),
	'rowB1Gfx': MaterialBinding("gfx_wtf_row_b1", MaterialRef("gfx_wtf_blue"), ModelRef("gfx_wtf_row_b1m")),
	'rowB2Gfx': MaterialBinding("gfx_wtf_row_b2", MaterialRef("gfx_wtf_blue"), ModelRef("gfx_wtf_row_b2m")),
	'rowB3Gfx': MaterialBinding("gfx_wtf_row_b3", MaterialRef("gfx_wtf_blue"), ModelRef("gfx_wtf_row_b3m")),
	'rowB5Gfx': MaterialBinding("gfx_wtf_row_b5", MaterialRef("gfx_wtf_blue"), ModelRef("gfx_wtf_row_b5m")),
	
	'goal': CompoundModel("pmodel_wtf_goal"),
	'goalMesh': Model("gfx_wtf_goal"),
	'goalGfx': MaterialBinding("gfx_table_base", MaterialRef("gfx_wtf_base"), ModelRef("gfx_wtf_goal")),
	'back': CompoundModel("pmodel_wtf_goal"),
	'backMesh': Model("gfx_wtf_back"),
	'backGfx': MaterialBinding("gfx_table_base", MaterialRef("gfx_wtf_base"), ModelRef("gfx_wtf_back")),
	
	'base': OblongModel("pmodel_table_base", Vector(w, h * chunkiness, d)),
	'baseMesh': PrimitiveModel("gfx_table_basem", Oblong(w, h * chunkiness, d)),
	'baseGfx': MaterialBinding("gfx_table_base", MaterialRef("gfx_wtf_base"), MeshRef("gfx_table_basem")),
	'side': OblongModel("pmodel_table_side", Vector(w, h, w * chunkiness)),
	'sideGfx': MaterialBinding("gfx_table_sideBinding", MaterialRef("gfx_wtf_base"), PrimitiveModel("gfx_table_side", Oblong(w, h, w * chunkiness))),
	'slope': OblongModel("pmodel_table_slope", Vector(w, slopeH, slopeD)),
	'slopeGfx': MaterialBinding("gfx_table_slopeBinding", MaterialRef("gfx_wtf_base"), PrimitiveModel("gfx_table_slope", Oblong(w, slopeH, slopeD))),
	'corner': OblongModel("pmodel_table_corner", Vector(cornerW, slopeH, cornerW)),
	'cornerGfx': MaterialBinding("gfx_table_cornerBinding", MaterialRef("gfx_wtf_base"), PrimitiveModel("gfx_table_corner", Oblong(cornerW, slopeH, cornerW)))
};

# Load all models into the engine. All concrete datatypes are strongly
# referenced so they will never be unloaded.
DebugPrint("Loading models into engine...");
for k,x in gModels.items():
	DebugPrint("Loading '" + k + "'");
	DataManager.GetInst().Insert(x);

DebugPrint("Articulating models...");

# Ball
gModels['ballMesh'].InsertPrimitive(Sphere(scale * 0.012, 16, 16), Vector(), Orientation());


# Rows
def CreateRowModels(numMen, gfx, pmodel, manMaterial):
	ori = Orientation.Rotation(Vector(1, 0, 0), Angle(pi / 2));
	pos = Vector(0,0,0);
	
	pmodel.InsertModel(gModels["bar"], pos, ori);
	gfx.InsertMesh(TransformedMesh(gModels["barGfx"], pos, ori));
	
	# Create Men
	ori = Orientation.Rotation(Vector(0,1,0), Angle(pi));
	pos.y -= yattach;
	pos.z = -(((ratio / 2) - (ratio / (numMen + 1))) * scale);
	
	for i in range(0, numMen):
		gfx.InsertMesh(TransformedMesh(gModels["manGfx"], pos, ori));
		pmodel.InsertModel(gModels["man"], pos, ori);
		pos.z = pos.z + ((ratio * scale) / (numMen + 1));

CreateRowModels(1, gModels["rowA1Mesh"], gModels["rowA1"], 0);
CreateRowModels(2, gModels["rowA2Mesh"], gModels["rowA2"], 0);
CreateRowModels(3, gModels["rowA3Mesh"], gModels["rowA3"], 0);
CreateRowModels(5, gModels["rowA5Mesh"], gModels["rowA5"], 0);
CreateRowModels(1, gModels["rowB1Mesh"], gModels["rowB1"], 0);
CreateRowModels(2, gModels["rowB2Mesh"], gModels["rowB2"], 0);
CreateRowModels(3, gModels["rowB3Mesh"], gModels["rowB3"], 0);
CreateRowModels(5, gModels["rowB5Mesh"], gModels["rowB5"], 0);


# Goals
ori = Orientation();
relPos = Vector(gd / 2, gh / 2, 0);
gModels["goalMesh"].InsertMesh(TransformedMesh(Oblong(gd * chunkiness, gh, gw), relPos, ori));
gModels["goal"].InsertModel(OblongModel("", Vector(gd * chunkiness, gh, gw)), relPos, ori);
relPos = Vector(0, -(gh * chunkiness) / 2, 0);
gModels["goalMesh"].InsertMesh(TransformedMesh(Oblong(gd, gh * chunkiness, gw), relPos, ori));
gModels["goal"].InsertModel(OblongModel("", Vector(gd, gh * chunkiness, gw)), relPos, ori);


# Back wall comprised of three sections surrounding the goal.
ori = Orientation();

relPos = Vector(gd / 2, (h + gh) / 2, 0);
gModels["backMesh"].InsertMesh(TransformedMesh(Oblong(gd, h - gh, gw), relPos, ori));
gModels["back"].InsertModel(OblongModel("", Vector(gd, h - gh, gw)), relPos, ori);

relPos = Vector(gd / 2, h / 2, (d + gw) / 4);
gModels["backMesh"].InsertMesh(TransformedMesh(Oblong(gd, h, (d - gw) / 2), relPos, ori));
gModels["back"].InsertModel(OblongModel("", Vector(gd, h, (d - gw) / 2)), relPos, ori);

relPos = Vector(gd / 2, h / 2, -(d + gw) / 4);
gModels["backMesh"].InsertMesh(TransformedMesh(Oblong(gd, h, (d - gw) / 2), relPos, ori));
gModels["back"].InsertModel(OblongModel("", Vector(gd, h, (d - gw) / 2)), relPos, ori);



#### Game Objects ####

class Ball(Physical):
	def __init__(self):
		opts = PhysicalCreationOptions();
		opts.name = "wtf_ball";
		Physical.__init__(self, opts, gModels["ballGfx"], gModels["ball"]);
		self.reset();
		
	def reset(self):
		self.Activate();
		self.position = Vector(0, 0.2 * scale, 0);
		self.velocity = Vector(0,0,0);
		self.ApplyImpulse(Vector(1,0,10), Vector(0,0,0));
		

class Row:
	def __init__(self, name, numMen, pos, flip, slide, table):
		
		opts = PhysicalCreationOptions();
		opts.name = "wtf_" + name;
		
		# slide = (d / (numMen * 3));
		pos.x = pos.x * scale;
		pos.y = pos.y * scale;
		
		self.startPos = Vector(pos.x, pos.y, 0);
		self.currentPos = 0;
		self.maxPos = slide;
		
		# Now create the compound bar
		self.bar = Physical(opts, gModels[name + "Gfx"], gModels[name]);
		self.bar.position = Vector(self.startPos.x, self.startPos.y, 0);
		if flip:
			self.bar.orientation = Orientation.Rotation(Vector(0,1,0), Angle(pi));
			flip = -1.0;
		else:
			flip = 1.0;
		
		# Add slide constraints
		SliderConstraint(table.side1, self.bar,
			Vector(self.startPos.x - table.side1.position.x, self.startPos.y - table.side1.position.y, 0),
			Vector(0, 0, (flip * d) / 2),
			Vector(0,0,-slide), Vector(0,0,slide));
		SliderConstraint(table.side2, self.bar,
			Vector(self.startPos.x - table.side2.position.x, self.startPos.y - table.side2.position.y, 0),
			Vector(0, 0, -(flip * d) / 2),
			Vector(0,0,-slide), Vector(0,0, slide));
	
	def move(self, amt):
		self.bar.Activate();
		self.bar.ApplyImpulse(Vector(0,0, amt * scale * 10), Vector(0,0,0));
		
	def rotate(self, amt):
		# amt = velocity of mouse in x direction
		self.bar.Activate();
		amt = amt * scale;
		self.bar.ApplyImpulse(Vector(-amt,0,0), Vector(0, 1, 0));
		self.bar.ApplyImpulse(Vector(amt,0,0), Vector(0, -1, 0));
	
	def moveTo(self, pos):
		self.bar.Activate();
		self.bar.ApplyImpulse(Vector(0,0, ((pos * self.maxPos) - self.bar.position.z) * 100), Vector(0,0,0));
		
	def rotateTo(self, angle):
		v = self.bar.orientation * Vector(0, -1, 0);
		d = angle - calcAngle(v.x, v.y);
		if d > (math.pi * 1.5):
			d -= math.pi;
			d = -d;
		elif d < -(math.pi * 1.5):
			d += math.pi;
			d = -d;
		self.rotate( d * 2.0 );
		
	def reset(self):
		self.bar.position = self.startPos;
		self.bar.velocity = Vector(0,0,0);

class Goal(Static):
	def __init__(self, name):
		opts = StaticCreationOptions();
		opts.name = name;
		Static.__init__(self, opts, gModels["goalGfx"], gModels["goal"]);
		
class BackWall(Static):
	def __init__(self, name):
		opts = StaticCreationOptions();
		opts.name = name;
		Static.__init__(self, opts, gModels["backGfx"], gModels["back"]);

# Table metaobject
class Table:
	def __init__(self):
		opts = WtfFx.PlanarReflectionCreationOptions();
		
		# Skybox
		opts.name = "wtf_skybox";
		self.skybox = Static(opts, Oblong(1000,1000,1000), PointModel(""));
		
		
		
		opts.name = "wtf_table_base";
		opts.planarNormal = Vector(0,1,0);
		#self.base = WtfFx.PlanarReflection(opts, gModels["baseGfx"], gModels["base"]);
		self.base = Static(opts, gModels["baseGfx"], gModels["base"]);
		
		opts.name = "wtf_table_side1";
		self.side1 = Static(opts, gModels["sideGfx"], gModels["side"]);
		self.side1.position = Vector(0, (h / 2) - (h / 8), (d - (w * chunkiness)) / 2);
		opts.name = "wtf_table_side2";
		self.side2 = Static(opts, gModels["sideGfx"], gModels["side"]);
		self.side2.position = Vector(0, (h / 2) - (h / 8), -(d - (w * chunkiness)) / 2);
		
		# Side slopes
		opts.name = "wtf_table_slope1";
		self.side1.slope = Static(opts, gModels["slopeGfx"], gModels["slope"]);
		self.side1.slope.position = Vector(0, 0, (d / 2) - (slopeD));
		self.side1.slope.rotation = Vector(0, -slopeAng, 0);
		opts.name = "wtf_table_slope2";
		self.side2.slope = Static(opts, gModels["slopeGfx"], gModels["slope"]);
		self.side2.slope.position = Vector(0, 0, -((d / 2) - (slopeD)));
		self.side2.slope.rotation = Vector(0, slopeAng, 0);
		
		# Corner slopes
		cslopeAng = slopeAng * (slopeD / cornerW);
		opts.name = "wtf_table_corner1";
		self.corner1 = Static(opts, gModels["cornerGfx"], gModels["corner"]);
		self.corner1.position = Vector((w - cornerW) / 2, 0, -((d - cornerW) / 2));
		self.corner1.rotation = Vector(cslopeAng, cslopeAng, 0);
		opts.name = "wtf_table_corner2";
		self.corner2 = Static(opts, gModels["cornerGfx"], gModels["corner"]);
		self.corner2.position = Vector(-(w - cornerW) / 2, 0, -((d - cornerW) / 2));
		self.corner2.rotation = Vector(-cslopeAng, cslopeAng, 0);
		opts.name = "wtf_table_corner3";
		self.corner3 = Static(opts, gModels["cornerGfx"], gModels["corner"]);
		self.corner3.position = Vector(-(w - cornerW) / 2, 0, ((d - cornerW) / 2));
		self.corner3.rotation = Vector(-cslopeAng, -cslopeAng, 0);
		opts.name = "wtf_table_corner4";
		self.corner4 = Static(opts, gModels["cornerGfx"], gModels["corner"]);
		self.corner4.position = Vector((w - cornerW) / 2, 0, ((d - cornerW) / 2));
		self.corner4.rotation = Vector(cslopeAng, -cslopeAng, 0);
		
		# Back walls and goals
		self.back1 = BackWall("wtf_table_back1");
		self.back1.position = Vector(w / 2, -baseH / 2, 0);
		self.back2 = BackWall("wtf_table_back2");
		self.back2.position = Vector(-w / 2, -baseH / 2, 0);
		self.back2.rotation = Vector(0,0,pi);
		
		self.goal1 = Goal("wtf_table_goal1");
		self.goal1.position = Vector(w / 2, -baseH / 2, 0);
		self.goal2 = Goal("wtf_table_goal1");
		self.goal2.position = Vector(-w / 2, -baseH / 2, 0);
		self.goal2.rotation = Vector(0,0,pi);
		
		y = 0.06;
		
		# Create rows (bars)
		self.rows = {
			0: Row("rowA1", 1, Vector(-0.40, y, 0), False, gw * 0.64, self),
			1: Row("rowA2", 2, Vector(-0.285, y, 0), False, 0.12 * scale, self),
			2: Row("rowA5", 5, Vector(-0.055, y, 0), False, 0.025 * scale, self),
			3: Row("rowA3", 3, Vector(0.17, y, 0), False, 0.08 * scale, self),
			4: Row("rowB1", 1, Vector(0.40, y, 0), True, gw * 0.64, self),
			5: Row("rowB2", 2, Vector(0.285, y, 0), True, 0.12 * scale, self),
			6: Row("rowB5", 5, Vector(0.055, y, 0), True, 0.025 * scale, self),
			7: Row("rowB3", 3, Vector(-0.17, y, 0), True, 0.08 * scale, self)
		};
		
	def reset(self):
		map(lambda row: self.rows[row].reset(), self.rows);
		

def calcAngle(x, y):
	if y == 0.0:
		return (x > 0) and (math.pi / 2) or -(math.pi / 2);
	elif y > 0.0:
		return math.atan(x / -y) + math.pi;
	else:
		return math.atan(x / -y);
